package core.proto;

import java.util.*;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import org.apache.commons.io.FilenameUtils;

/* Taken from the Tavena project. 
 * @see http://taverna.googlecode.com/svn/taverna/ui/net.sf.taverna.t2.ui-activities/trunk/biomoby-activity-ui/src/main/java/net/sf/taverna/t2/activities/biomoby/ui/
 * 
 * This class implements the functionality of filtering out tree nodes 
 * based on some predicate. The class is used to hide nodes that do not
 * match the result of search. 
 */
public final class FilterTreeModel extends DefaultTreeModel {

	private static final long serialVersionUID = 8446366558654481274L;
	/**
	 * What absolute paths are acceptable. 
	 */
	Set<String> acceptedPaths;

	/**
	 *
	 * @param node
	 *            the node to apply filtering to
	 */
	public FilterTreeModel(FilterTreeNode node) {
		this(node, null);
	}

	/**
	 *
	 * @param node
	 *            the node to apply filtering to
	 * @param filter
	 *            the actual filter we will apply
	 */
	public FilterTreeModel(FilterTreeNode node, Set<String> filter) {
		super(node);
		acceptedPaths = filter;
		node.setAcceptedPaths(filter);
	}

	/**
	 *
	 * @param filter
	 *            the filter to set and apply to our node
	 */
	public void setAcceptedPaths(Set<String> filter) {
		if (root != null) {
			acceptedPaths = filter;
			((FilterTreeNode) root).setAcceptedPaths(filter);
			Object[] path = { root };
			fireTreeStructureChanged(this, path, null, null);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * javax.swing.tree.DefaultTreeModel#getChildCount(java.lang.Object)
	 */
	public int getChildCount(Object parent) {
		if (parent instanceof FilterTreeNode) {
			return (((FilterTreeNode) parent).getChildCount());
		}
		return 0;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.swing.tree.DefaultTreeModel#getChild(java.lang.Object,
	 * int)
	 */
	public Object getChild(Object parent, int index) {
		if (parent instanceof FilterTreeNode) {
			return (((FilterTreeNode) parent).getChildAt(index));
		}
		return null;
	}

	/**
	 * @return the filter that we are currently using
	 */
	public Set<String> getAcceptedPaths() {
		return acceptedPaths;
	}
}

class FilterTreeNode extends DefaultMutableTreeNode {
	private static final long serialVersionUID = -5269485070471940445L;
	private Set<String> acceptedPaths;
	private boolean passed = true;
	private List<FilterTreeNode> filteredChildren = new ArrayList<FilterTreeNode>();

	public FilterTreeNode(Object userObject) {
		super(userObject);
	}

	public Set<String> getAcceptedPaths() {
		return acceptedPaths;
	}

	public void setAcceptedPaths(Set<String> filter) {
		this.acceptedPaths = filter == null ? new HashSet<String>() : filter;
		passed = false;
		filteredChildren.clear();
		if (filter == null) {
			passed = true;
			passFilterDown(null);
		} else if (pass(this)) {
			passed = true;
			passFilterDown(filter);
		} else {
			passFilterDown(filter);
			passed = filteredChildren.size() != 0;
		}
	}

	/**
	 * Whether to accept the node. 
	 * @param node
	 * @return
	 */
	private boolean pass(FilterTreeNode node) {
		if (acceptedPaths == null)
			return true;
		
		if (node.getUserObject() instanceof IconData && ((IconData) node.getUserObject()).m_data instanceof CaseNode){
			CaseNode caseNode = ((CaseNode) ((IconData) node.getUserObject()).m_data);
			
			return acceptedPaths.contains(FilenameUtils.getBaseName(caseNode.getAbsolutePath()));
		}else{
			throw new Error("Design of file tree has changed"); 
		}
	}

	private void passFilterDown(Set<String> filter) {
		int realChildCount = super.getChildCount();
		for (int i = 0; i < realChildCount; i++) {
			FilterTreeNode realChild = (FilterTreeNode) super.getChildAt(i);
			realChild.setAcceptedPaths(filter);
			if (realChild.isPassed()) {
				filteredChildren.add(realChild);
			}
		}
	}

	public void add(FilterTreeNode node) {
		super.add(node);
		node.setAcceptedPaths(acceptedPaths);
		if (node.isPassed()) {
			filteredChildren.add(node);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see javax.swing.tree.DefaultMutableTreeNode#remove(int)
	 */
	 public void remove(int childIndex) {
		 if (acceptedPaths != null) {
			 // as child indexes might be inconsistent..
			 throw new IllegalStateException(
			 "Can't remove while the filter is active");
		 }
		 super.remove(childIndex);
	 }

	 /*
	  * (non-Javadoc)
	  *
	  * @see javax.swing.tree.DefaultMutableTreeNode#getChildCount()
	  */
	 public int getChildCount() {
		 if (acceptedPaths == null) {
			 return super.getChildCount();
		 }
		 return (filteredChildren.size());
	 }

	 /*
	  * (non-Javadoc)
	  *
	  * @see javax.swing.tree.DefaultMutableTreeNode#getChildAt(int)
	  */
	 public FilterTreeNode getChildAt(int index) {
		 if (acceptedPaths == null) {
			 return (FilterTreeNode) super.getChildAt(index);
		 }
		 return filteredChildren.get(index);
	 }

	 /**
	  *
	  * @return
	  */
	 public boolean isPassed() {
		 return passed;
	 }
}
